import React, { PureComponent } from "react"
const { Children: { map }, cloneElement: clone } = React
import PropTypes from "prop-types"
import cls from "classnames"
import item from "./item"
import { CLASS_PREFIX as cp } from "../const"
import { validate } from "./utils/validators"
import { wirte } from "./utils/readwirte"

export default class HYform extends PureComponent {
  static isForm = true
  state = {
    model: this.props.model || {},
  }
  multChildItem = {}
  validators = {}
  validateResult = {}
  validateItem = (field, value) => {
    const { validators, validateResult, multChildItem } = this

    if (validators[field]) {
      let child = multChildItem[field]
      if (child) {
        if (value) {
          for (let k = 0, len = child.length; k < len; k++) {
            validateResult[field] = validate(validators[field], value[k])
            if (validateResult[field]) {
              break
            }
          }
        } else {
          validateResult[field] = validate(validators[field], value)
        }
      } else {
        validateResult[field] = validate(validators[field], value)
        this.forceUpdate()
      }
    }
  }
  validateForm = () => {
    const { validators, state: { model }, props: { onFormChange } } = this
    Object.entries(validators).forEach(([k]) => {
      this.validateItem(k, model[k])
    })
    if (onFormChange) onFormChange(model, this.validateResult)
    return this.isValid()
  }

  isValid() {
    return !Object.entries(this.validateResult).some(([, v]) => v != null)
  }
  modelChange = async (field, value = null) => {
    const { model } = this.state,
      { onFormChange, onChange } = this.props,
      newModel = wirte(model, field, value)
    this.validateItem(field, value)
    await this.setState({
      model: newModel
    })
    if (onFormChange) onFormChange(newModel, this.validateResult)
    if (onChange) onChange(this.state.model)
  }
  cloneChildren = (Comp) => {
    const { children } = Comp.props
    return map(children, (child) => {
      if (child && child.type) {
        const { type } = child
        if (type.isFormItem) {
          return this.cloneItem(child)
        }
        if (type.isForm) {
          return this.cloneForm(child)
        }
        return clone(child, {}, this.cloneChildren(child))
      }
      return child
    })
  }
  cloneItem = (Comp) => {
    const { props: { validator = [], exclude = [], name, multChild } } = Comp,
      { props: { validators: comval = [] }, state: { model }, validators, validateResult, modelChange } = this
    if (name) {
      validators[name] = comval.concat(validator).filter(i => !exclude.some(e => e === i))
    }
    let multChildItem = {}
    if (multChild) {
      let info = {
        field: name
      }
      this.multChildItem[name] = info
      multChildItem = { multChildItem: info }
    }
    return clone(Comp, {
      modelChange,
      model,
      ...multChildItem,
      isRequire: (validators[name] || []).find((v) => v === "require"),
      validateInfo: validateResult[name],
    })
  }
  cloneForm = (Comp) => {
    const { props: { validator = [], exclude } } = Comp,
      { validator: comval = [] } = this.props

    return clone(Comp,
      exclude
        ? {}
        : { validator: comval.concat(validator) }
    )
  }
  initModel = (field) => {
    this.state.model[field] = null
  }
  getValues = () => {
    return this.state.model
  }
  cleanModel = () => {
    this.state.model = {}
  }
  render() {
    return (
      <div
        className={cls(`${cp}-form`, this.props.cs)}>
        {this.cloneChildren(this)}
      </div>
    )
  }
  componentWillReceiveProps({ model }) {
    if (model !== this.props.model) {
      this.setState({
        model
      })
    }
  }
  static propTypes = {
    model: PropTypes.object,        //可控的表单数据
    validators: PropTypes.arrayOf(
      PropTypes.oneOfType([         //校验规则,会追加到所有Item上
        PropTypes.string,           //已内置校验
        PropTypes.func,             //自定义函数校验,函数会接收输入控件的value,如果校验通过return null， 未通过return {message：提示文字}
        PropTypes.object            //{rules: 校验正则， message：提示文字}
      ])
    ),
    onFormChange: PropTypes.func,   //form表单数据改变时执行回调
    cs: PropTypes.oneOfType([       //指定容器class
      PropTypes.string,
      PropTypes.object
    ])
  }
}

export const Item = item
